TWEEN_BUILDER = function(params){
	var PARAMS = params;
	var _this = this;
	
	var _ultimo_punto_linea_animada = null;
	
	//TODO _listaFrames esta por ahora por el refactoring
	var _listaFrames = [];
	this.setListaFrames = function(lista){ _listaFrames = null; _listaFrames = lista;};
	
	var mantenerCamara3D = false;
	this.setMantenerCamara3D = function(value){mantenerCamara3D = value;}; 
	
	var _estadoAnimacion = null;
	this.setEstadoAnimacion = function(estado){_estadoAnimacion = estado;};
	
	var _controlCamaraFly = null;
	this.setControlCamara = function(controlCamara){_controlCamaraFly = controlCamara;};
	
	//***********************************************************************************************************
	//***********************************************************************************************************
	// 											  FUNCIONES PUBLICAS
	//***********************************************************************************************************
	//***********************************************************************************************************
	//***********************************************************************************************************
	
	
	
	
	//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	//TODO: las coordenadas de los puntos estan como vienen del servidor X = X, Y = Z, Z = Y
	//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	this.getAnimacionPlano = function(indice, duracionTrayecto, verticeActual){
								
				//asigno punto a variable de contexto (global?)
				_ultimo_punto_linea_animada = _listaFrames[indice];
	
				
				//EN ESTE SWITCH MARCO LAS DIFERENCIAS EN LAS ANIMACIONES
				switch (_estadoAnimacion.getTipoAnimacion()){
				
					case "FROM_ABOVE_STAIRS_COMPLETE_ANIM" :
					case "FROM_ABOVE_STAIRS_SIMPLE_ANIM":
					case "FROM_ABOVE_NO_STAIRS_ANIM":
						
						return getAnimacionPlanoFromAbove(indice, duracionTrayecto, verticeActual);
						break;
					
					case "FROM_ABOVE_STAIRS_COMPLETE_ANIM_FOLLOW" :
						
						if (!mantenerCamara3D){
							return getAnimacionPlanoFromAbove(indice, duracionTrayecto, verticeActual);						
						}
						
						return getAnimacionPlanoChaseReposition(indice, duracionTrayecto, verticeActual);
						
						break;
						
					case "CHASE_REPOSITION_POINTS" :
						
						return getAnimacionPlanoChaseReposition(indice, duracionTrayecto, verticeActual);
						break;
						
					case "CHASE_ALL" :
							
						return getAnimacionPlanoChase(indice,duracionTrayecto, verticeActual);
						break;
						
					case  "LOOK_AT": 
						
						return getAnimacionPlanoLookAt(indice, duracionTrayecto, verticeActual);
						break;
						
					default : return null;
			};	
	};
	
	
	this.getAnimacionEscaleras = function(indice, duracionTrayecto, verticeActual){
		
		switch (_estadoAnimacion.getTipoAnimacion()){
			case "FROM_ABOVE_STAIRS_COMPLETE_ANIM" :
			case "CHASE_REPOSITION_POINTS" :
			case "FROM_ABOVE_STAIRS_COMPLETE_ANIM_FOLLOW" :
				return getAnimacionEscaleras_desdeArriba(indice, duracionTrayecto, verticeActual, true);
				break;
			case "CHASE_ALL" :
				return getAnimacionEscalerasChase(indice, duracionTrayecto, verticeActual, true);
				break;
			case "FROM_ABOVE_STAIRS_SIMPLE_ANIM":
				return getAnimacionEscaleras_desdeArriba(indice, duracionTrayecto, verticeActual, false);
				break;
			case "FROM_ABOVE_NO_STAIRS_ANIM":
			case  "LOOK_AT": 
				return getAnimacionEscalerasComun(indice, duracionTrayecto, verticeActual);
				break;
				
			default : return null; 
		}		
	};
		
	 
	
	//para la animacion de los marcadores (flechas) q indican el comienzo y fin del camino
	this.getTweenMarcador = function(tipo, duracion){
		var webglObject = null;
		var index = 0;
		
		if(tipo == "inicial"){
			webglObject = WEBGL.getMarcadorInicio();
			index = 0;
		}else{ //final
			webglObject = WEBGL.getMarcadorFinal();
			index = _listaFrames.length - 1;
		}
		
		var posInicial = new THREE.Vector3(_listaFrames[index].x, _listaFrames[index].z + 500, _listaFrames[index].y);
		var posFinal = new THREE.Vector3(_listaFrames[index].x, _listaFrames[index].z + 20, _listaFrames[index].y);
		return getAnimacionMarcador(webglObject, posInicial, posFinal, duracion);
	};
	
	
	//setea la opacidad del piso pasado como parametro en 1, mientras que al que era activo antes, le pone opacidad 0
//	this.cambiarOpacidadNiveles = function(nombreEdificio, nombreNivel, animarOpacidad, duracion, callback){
	//nivelesVisibles --> niveles a los que se seteara el valor de opacidad = 1
	//nivelesInvisibles --> niveles a los que se seteara el valor de opacidad = 0
	this.cambiarOpacidadNiveles = function(nivelesVisibles, nivelesInvisibles, animarOpacidad, duracion, callback){
		
		//alert("CAMBIAR OPACIDAD NIVELES. falta implementar"); return null;
		
		var animar = (animarOpacidad == undefined )? false : animarOpacidad;
		if (duracion == 0){animar = false;}
		
		if (animar){
			
					
			//console.log("nivelAnterior",nivelAnterior,"nivelActual",nivelActual)
			//si animo el cambio de opacidad
			if (true /*nivelAnterior != null && nivelAnterior != nivelActual*/){ //si el nivel anterior y siguiente no son iguales
				 var t1 = new TWEEN.Tween({x:0, y:1})
				.to( {x:1, y:0},duracion)
				.onUpdate(function(){ 
					for (var i = 0, l = nivelesVisibles.length; i<l; i++){
						UTILS.setOpacity(nivelesVisibles[i], this.x);
					}
					for (var i = 0, l = nivelesInvisibles.length; i<l; i++){
						UTILS.setOpacity(nivelesInvisibles[i], this.y);
					}
				})			
				.onComplete(function(){								 
						 if (callback) callback();});
						 
				 return t1;
			}else{ //si el nivel anterior y siguiente son iguales (o el anterior es nulo), 
				   //no animo la opacidad (solo ejecuto 'callback', si es que esta definida)

				if (callback){
					var t2 = new TWEEN.Tween({x:0}).to({x:1},1).onComplete(function(){if (callback) callback();});
					return t2;
				}else{
					return null;
				}
			}
		}else{
			for (var i = 0, l = nivelesVisibles.length; i<l; i++){
				UTILS.setOpacity(nivelesVisibles[i], 1);
			}
			for (var i = 0, l = nivelesInvisibles.length; i<l; i++){
				UTILS.setOpacity(nivelesInvisibles[i], 0);
			}
			if (callback) callback();
			return null;
		}
	};
	
	
	//***********************************************************************************************************
	//***********************************************************************************************************
	// 											  FUNCIONES PRIVADAS
	//***********************************************************************************************************
	//***********************************************************************************************************
	//***********************************************************************************************************
	
	
	
	//---------------------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------------------
	//----------------------------------- ANIMACIONES MARCADORES INICIO Y FINAL--------------------------------------------
	//---------------------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------------------
	
	//coordenadas X = X, Y = Y, Z = Z
	function getAnimacionMarcador(webglObject, posInicial, posFinal, duracion, callback){
		
		var tweenFlecha = null;
		var easing = TWEEN.Easing.Bounce.EaseInOut;
			
		webglObject.visible = true;
		
		if (duracion == undefined || duracion == null || !duracion){ //no animar el marcador, solo mostrarlo
			webglObject.position.x = posFinal.x;
			webglObject.position.y = posFinal.y;
			webglObject.position.z = posFinal.z;
			
		}else{
			webglObject.position.x = posInicial.x;
			webglObject.position.y = posInicial.y;
			webglObject.position.z = posInicial.z;
			tweenFlecha = new TWEEN.Tween( webglObject.position )
								.to( {	
									x: posFinal.x,
									y: posFinal.y,
									z: posFinal.z
									},//TODO
									1000)//TODO
								.easing( easing )
								.onComplete(function(){if(callback) callback();});
		}
		
		return tweenFlecha;
	};
	
	
	//---------------------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------------------
	//----------------------------------------- ANIMACIONES ESCALERAS -----------------------------------------------------
	//---------------------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------------------
	
	
	//completo = si hago una rotacion completa o solo roto la camara en 3D que quede mirando al norte
	function getAnimacionEscaleras_desdeArriba(indice, duracionTrayecto, verticeActual, completo){
	
		
			//1) acomodo la camara
			//2) animo camino a traves de la escalera y voy pintando la escalera
			//3) animo la camara otra vez a la posicion inicial
			//4) llamo a dibujarPathCamino incrementando el indice
			PARAMS.ralentizar_anim_escaleras = 3;//TODO
			duracionTrayecto *= PARAMS.ralentizar_anim_escaleras;
			
			
			//******************************************
			// TWEEN: POSICIONAR CAMARA INICIAL
			
			//CALCULOS PARA ROTAR LA ESCALERA!!!!!!!!!!!!!!!
			var direccionEscalera = new THREE.Vector3(0,0,-1), rotY = 0, pitch, nuevoPunto;
			
			//<<TODO: calcular el angulo de rotacion respecto al vector (0,0,-1)
			if (completo){ //calculo la rotacion para que la camara quede enfrentando a la escalera
				
				//calculo la direccion en que crece la escalera (proyectada en el plano XZ)
				 direccionEscalera = new THREE.Vector3(
						_listaFrames[indice].x - _ultimo_punto_linea_animada.x,
						0,
						_listaFrames[indice].y - _ultimo_punto_linea_animada.y
					).normalize(); //proyectada en plano XZ
				
				 if (_listaFrames[indice].z < _ultimo_punto_linea_animada.z){ //escalera que baja
						direccionEscalera.negate();
				 }
				 
				 rotY = Math.acos((new THREE.Vector3(1,0,0)).dot(direccionEscalera));
									
					if (direccionEscalera.x>=0){
						if (direccionEscalera.z>=0){ rotY = -Math.PI/2 - rotY;}
						else{rotY = -Math.PI/2 + rotY;}
					}else{
						if (direccionEscalera.z>=0){ rotY = Math.PI - (rotY - Math.PI/2);}
						//else{rotY =  (Math.PI - rotY) - Math.PI/2 ;}	
						
						
						else{
//							alert("rotY "+ rotY); 
							//console.warn("rotY ", rotY); 
							rotY =  Math.PI/2 - (Math.PI - rotY)  ;}					
//						rotY =  (Math.PI - rotY) - Math.PI/2 ;}					
					}	
					
					
			}
						
			//calculo el punto al que va a para la camara
			nuevoPunto = direccionEscalera.clone();
			nuevoPunto.multiplyScalar(PARAMS.distancia_a_escalera).addSelf(new THREE.Vector3(_ultimo_punto_linea_animada.x, _ultimo_punto_linea_animada.z, _ultimo_punto_linea_animada.y));
			nuevoPunto.y += Math.abs(PARAMS.distancia_a_escalera);
			pitch = -Math.PI/4; //importante!! es PI/4 porque la distancia y altura al punto son iguales
			
			//console.log("111camara position",_controlCamaraFly.object.position);
			//console.log("111nuevoPunto",nuevoPunto);
			
			var tiempo = PARAMS.tiempo_posicionamiento_camara;
			if (true || _estadoAnimacion.getTipoAnimacionCamara() == "VARIABLE"){	
				var vectorDireccion = nuevoPunto.clone();
				vectorDireccion.subSelf(_controlCamaraFly.object.position);
				var dist = vectorDireccion.length();
				tiempo = dist/PARAMS.velocidadRepCamara;	
				
			}	
			
				var tweenPosicionarCamara = _controlCamaraFly.posicionarCamara(nuevoPunto, rotY, pitch, tiempo, null);
				//console.log("111camara position",_controlCamaraFly.object.position);
			//------------------------------------------
			
			
			//******************************************
			// TWEEN: ANIMAMOS COLOR DE LA ESCALERA		
				
				var tweenColorEsc = getTweenColorEscalera(indice, duracionTrayecto);			
			//------------------------------------------	
				
				
				
			//******************************************
			// TWEEN: ANIMAMOS LA LINEA Y LA CAMARA A LA VEZ		
			
				//asigno punto a variable de contexto (global?)
				_ultimo_punto_linea_animada = _listaFrames[indice];
				
				//calculo la posicion final de la camara a medida que se realiza la animacion de la escalera
				//teniendo en cuenta la direccion en la que esta desplagada
				var otroPunto = direccionEscalera.clone();
				otroPunto.multiplyScalar(PARAMS.distancia_a_escalera).addSelf(new THREE.Vector3(_listaFrames[indice].x,_listaFrames[indice].z,_listaFrames[indice].y));
				otroPunto.y += Math.abs(PARAMS.distancia_a_escalera);
				
				
				//la camara y la linea se animan en paralelo (el tiempo de reposicionamiento de la camara tiene que
				//ser igual al de la linea)
				tweenAnimacionLineaCamara = new TWEEN.Tween( 
							{
								x: verticeActual.position.x,
								y: verticeActual.position.y,
								z: verticeActual.position.z,
								
								x_camara : nuevoPunto.x,
								y_camara : nuevoPunto.y,
								z_camara : nuevoPunto.z,
								
							})
					.to(
							{	
								x: _listaFrames[indice].x,
								y: _listaFrames[indice].z + PARAMS.distancia_linea_a_plano,
								z: _listaFrames[indice].y,
								
								x_camara : otroPunto.x,
								y_camara : otroPunto.y,
								z_camara : otroPunto.z,
							},
							duracionTrayecto )
					.easing( TWEEN.Easing.Linear.None )
					.onUpdate(function(){
						
						verticeActual.position.x = this.x;
						verticeActual.position.y = this.y;
						verticeActual.position.z = this.z;
						
						_controlCamaraFly.object.position.x = this.x_camara;
						_controlCamaraFly.object.position.y = this.y_camara;
						_controlCamaraFly.object.position.z = this.z_camara;
						
						_lineaAnimada.geometry.__dirtyVertices = true;
						
					});
				
			//------------------------------------------	
				
				
			//******************************************
			// TWEEN: DUMMY PARA HACER QUE LA "ANIMACION DE LA ESCALERA" Y "LINEA Y CAMARA" 
			// ARRANQUEN SINCRONIZADAS
			
			var tweenDummy = new TWEEN.Tween({}).to({}, 0)							
			.onComplete(function(){
					tweenColorEsc.start();
					tweenAnimacionLineaCamara.start();
			});	
			//------------------------------------------	
			
			
			
			//******************************************
			// TWEEN: DELAY PARA DETENER UNOS INSTANTES LA ANIMACION
			
			var tweenDelay = new TWEEN.Tween({}).delay(750).to({}, 0);							
			//------------------------------------------	
			
			
			
			//******************************************
			// TWEEN: RETORNAR LA CAMARA A LA POSICION ORIGINAL
			//OBS: tengo que llamar a 'posicionarCamara' de adentro de un dummy, porque sino el tween que se obtiene
			//corresponde a la instancia en que se encuentra la camara cuando se creo el tween
			
			var callbackSeguirConLinea = function(){
				//if (indice < indiceFinal)
				{
					ANIMATOR.animarKeyframe(++indice);
				}
			};
			
			var tweenDummy2 = new TWEEN.Tween({}).to({}, 0)							
			.onComplete(function(){
				
				//armo el punto al que se retorna luego de la animacion de la escalera
				var punto = {x: _ultimo_punto_linea_animada.x,
							 y: _ultimo_punto_linea_animada.z + PARAMS.distancia_a_objetivo,
							 z: _ultimo_punto_linea_animada.y};
				
				_controlCamaraFly.posicionarCamara(punto, 0, -Math.PI/2, PARAMS.tiempo_posicionamiento_camara, callbackSeguirConLinea).start();	
										
			});	
			//------------------------------------------
			
			
			//------------------------------------------		
//			tweenPosicionarCamara.chain(tweenDummy);
//			tweenDummy.chain(tweenDelay);
//			tweenDelay.chain(tweenDummy2);//reposiciono la camara
			
			switch (_estadoAnimacion.getTipoAnimacion()){
			case "FROM_ABOVE_STAIRS_COMPLETE_ANIM" :				
			case "FROM_ABOVE_STAIRS_SIMPLE_ANIM":
					tweenPosicionarCamara.chain(tweenDummy);
					tweenDummy.chain(tweenDelay);
					tweenDelay.chain(tweenDummy2);//reposiciono la camara
					return tweenPosicionarCamara;

				break;
			case "CHASE_REPOSITION_POINTS" : // no reposiciono la camara luego de la animacion
				//TODO controlar cuando reposiciono y cuando no!!!
					
					tweenPosicionarCamara.chain(tweenDummy);
					//tweenDummy.chain(tweenDelay);
//					tweenAnimacionLineaCamara.chain(tweenDelay);
//					tweenDelay.onComplete(function(){callbackSeguirConLinea();});
					tweenAnimacionLineaCamara.onComplete(function(){callbackSeguirConLinea();});
					return tweenPosicionarCamara;
				break;
			case "FROM_ABOVE_STAIRS_COMPLETE_ANIM_FOLLOW" : 
				
				//alert("falta implementar");//TODO
				var bool = isEscaleraCerca(indice);
//				console.log("bool ", bool);
				if (bool){
					mantenerCamara3D = true;
					tweenPosicionarCamara.chain(tweenDummy);
					tweenAnimacionLineaCamara.onComplete(function(){callbackSeguirConLinea();});
					return tweenPosicionarCamara; 
				}else{
					mantenerCamara3D = false;
					tweenPosicionarCamara.chain(tweenDummy);
					tweenDummy.chain(tweenDelay);
					tweenDelay.chain(tweenDummy2);//reposiciono la camara
					return tweenPosicionarCamara;
				}
				
				
				break;
			default : return null; 
			
			
			}
	}
	
	//se asume q indice indica el frame actual en que hay una escalera
	//indice + 1 tendra sera el frame de finalizacion de la escalera
	//indice + 2, si existe, sera el siguiente frame valido
	function isEscaleraCerca(indice){
		
		if (!(_listaFrames.length > indice +2)) return false;
		
		proximoIndice = indice + 2;
			
		
		var cantSpacesIntermedios = 3; //politica adoptada: si hay a lo sumo 'n' espacios entre dos escaleras, esta "cerca"
		var count = 0;
		for (var i = proximoIndice, l = _listaFrames.length; i < l; i++ ){
			count++;
			if (count > cantSpacesIntermedios){break;}//no hay una escalera cerca
			
			if (_listaFrames[i].tipo == "escalera"){
				return true;
			}
		}
		
		return false;
	}
	
	
	
	
	//completo = si hago una rotacion completa o solo roto la camara en 3D que quede mirando al norte
	function getAnimacionEscalerasChase(indice, duracionTrayecto, verticeActual, completo){
		
		PARAMS.ralentizar_anim_escaleras = 3;//TODO
		duracionTrayecto *= PARAMS.ralentizar_anim_escaleras;
		
		//******************************************
		// TWEEN: POSICIONAR CAMARA INICIAL
		
		//CALCULOS PARA ROTAR LA ESCALERA!!!!!!!!!!!!!!!
		var direccionEscalera = new THREE.Vector3(0,0,-1), rotY = 0, pitch, nuevoPunto;
		//calculo la direccion en que crece la escalera (proyectada en el plano XZ)
		direccionEscalera = new THREE.Vector3(
				_listaFrames[indice].x - _ultimo_punto_linea_animada.x,
				0,
				_listaFrames[indice].y - _ultimo_punto_linea_animada.y
		).normalize(); //proyectada en plano XZ
		
		
		//******************************************
		// TWEEN: ANIMAMOS COLOR DE LA ESCALERA		
		var tweenColorEsc = getTweenColorEscalera(indice, duracionTrayecto);			
		//------------------------------------------	
		
		
		//calculo el punto al que va a para la camara
		nuevoPunto = direccionEscalera.clone();
		nuevoPunto.multiplyScalar(PARAMS.distancia_a_escalera).addSelf(new THREE.Vector3(_ultimo_punto_linea_animada.x, _ultimo_punto_linea_animada.z, _ultimo_punto_linea_animada.y));
		nuevoPunto.y += Math.abs(PARAMS.distancia_a_escalera);
		
		var tweenPosicionarCamara = _controlCamaraFly.posicionarCamaraTarget(nuevoPunto,new THREE.Vector3(_ultimo_punto_linea_animada.x, _ultimo_punto_linea_animada.z, _ultimo_punto_linea_animada.y), 5000, null);
		
		//******************************************
		// TWEEN: ANIMAMOS LA LINEA Y LA CAMARA A LA VEZ		
		
		//asigno punto a variable de contexto (global?)
		_ultimo_punto_linea_animada = _listaFrames[indice];
		
		//calculo la posicion final de la camara a medida que se realiza la animacion de la escalera
		//teniendo en cuenta la direccion en la que esta desplagada
		var otroPunto = direccionEscalera.clone();
		otroPunto.multiplyScalar(PARAMS.distancia_a_escalera).addSelf(new THREE.Vector3(_listaFrames[indice].x,_listaFrames[indice].z,_listaFrames[indice].y));
		otroPunto.y += Math.abs(PARAMS.distancia_a_escalera);
		
		
		//la camara y la linea se animan en paralelo (el tiempo de reposicionamiento de la camara tiene que
		//ser igual al de la linea)
		tweenAnimacionLineaCamara = new TWEEN.Tween( 
				{
					x: verticeActual.position.x,
					y: verticeActual.position.y,
					z: verticeActual.position.z,
					
					x_camara : _controlCamaraFly.object.position.x,
					y_camara : _controlCamaraFly.object.position.y,
					z_camara : _controlCamaraFly.object.position.z,
//					x_camara : nuevoPunto.x,
//					y_camara : nuevoPunto.y,
//					z_camara : nuevoPunto.z,
					
				})
		.to(
				{	
					x: _listaFrames[indice].x,
					y: _listaFrames[indice].z + PARAMS.distancia_linea_a_plano,
					z: _listaFrames[indice].y,
					
					x_camara : otroPunto.x,
					y_camara : otroPunto.y,
					z_camara : otroPunto.z,
				},
				duracionTrayecto )
				.easing( TWEEN.Easing.Linear.None )
				.onUpdate(function(){
					
					verticeActual.position.x = this.x;
					verticeActual.position.y = this.y;
					verticeActual.position.z = this.z;
					
					_controlCamaraFly.object.position.x = this.x_camara;
					_controlCamaraFly.object.position.y = this.y_camara;
					_controlCamaraFly.object.position.z = this.z_camara;
					
					_controlCamaraFly.lookAtTarget({x : this.x, y: this.y, z: this.z});
					_lineaAnimada.geometry.__dirtyVertices = true;
					
				});
		
		//------------------------------------------	
		
		
		//******************************************
		// TWEEN: DUMMY PARA HACER QUE LA "ANIMACION DE LA ESCALERA" Y "LINEA Y CAMARA" 
		// ARRANQUEN SINCRONIZADAS
		
		var tweenDummy = new TWEEN.Tween({}).to({}, 0)							
		.onComplete(function(){
			tweenColorEsc.start();
			tweenAnimacionLineaCamara.start();
		});	
		//------------------------------------------	
		
		//******************************************
		// TWEEN: DELAY PARA DETENER UNOS INSTANTES LA ANIMACION
		
		var tweenDelay = new TWEEN.Tween({}).delay(750).to({}, 0);							
		//------------------------------------------	
		
		//******************************************
		// TWEEN: RETORNAR LA CAMARA A LA POSICION ORIGINAL
		//OBS: tengo que llamar a 'posicionarCamara' de adentro de un dummy, porque sino el tween que se obtiene
		//corresponde a la instancia en que se encuentra la camara cuando se creo el tween
		
		var callbackSeguirConLinea = function(){
			//if (indice < indiceFinal)
			{
				ANIMATOR.animarKeyframe(++indice);
			}
		};
		
//			tweenPosicionarCamara.chain(tweenDummy);
//			tweenPosicionarCamara.chain(tweenDummy);
			tweenAnimacionLineaCamara.onComplete(function(){callbackSeguirConLinea();});
			return tweenDummy;
		
		
	}
	
	
		
	//anima el color de la escalera
	//la animacion de la linea es la misma que para el plano (comun)
	function getAnimacionEscalerasComun(indice, duracionTrayecto, verticeActual){
			
		PARAMS.ralentizar_anim_escaleras = 3;//TODO
		duracionTrayecto *= PARAMS.ralentizar_anim_escaleras;
		
			var tweenColorEsc = getTweenColorEscalera(indice, duracionTrayecto);
			var tweenLinea = _this.getAnimacionPlano(indice, duracionTrayecto, verticeActual);
			
			var tween = new TWEEN.Tween({}).to({}, 0)							
			.onComplete(function(){
					tweenColorEsc.start();
					tweenLinea.start();
			});	
			
			return tween;
	}
	
	

	
	
	
	
	//---------------------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------------------
	//----------------------------------------- ANIMACIONES EN EL PLANO ---------------------------------------------------
	//---------------------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------------------
	//---------------------------------------------------------------------------------------------------------------------
	function getAnimacionPlanoFromAbove(indice, duracionTrayecto, verticeActual){
		//la camara y la linea se animan en paralelo (el tiempo de reposicionamiento de la camara tiene que
		//ser igual al de la linea)
		tweenAnimacionPlano = new TWEEN.Tween( 
					{
						x: verticeActual.position.x,
						y: verticeActual.position.y,
						z: verticeActual.position.z,
						
						//x_camara : _controlCamaraFly.object.position.x,
						y_camara : _controlCamaraFly.object.position.y,
						//z_camara : _controlCamaraFly.object.position.z,
						
					})
			.to(
					{	
						x: _listaFrames[indice].x,
						y: _listaFrames[indice].z + PARAMS.distancia_linea_a_plano,
						z: _listaFrames[indice].y,
						
						//x_camara : _listaFrames[indice].x,
						y_camara : _listaFrames[indice].z + PARAMS.distancia_a_objetivo,
						//z_camara : _listaFrames[indice].y,
					},
					duracionTrayecto )
//			.easing( TWEEN.Easing.Quadratic.InOut )
			.easing( TWEEN.Easing.Linear.None )
			.onUpdate(function(){
				verticeActual.position.x = _controlCamaraFly.object.position.x = this.x;
				verticeActual.position.y = this.y;
				verticeActual.position.z = _controlCamaraFly.object.position.z = this.z;
				_lineaAnimada.geometry.__dirtyVertices = true;
				_controlCamaraFly.object.position.y = this.y_camara;
				
				
				
			})
			.onComplete(
					//cuando finaliza la animacion, si existen mas _listaFrames de la polilinea (dentro del piso)
					// a animar, se continua animando el resto de los _listaFrames
					function(){
						//if (indice < indiceFinal)
						{
							ANIMATOR.animarKeyframe(++indice);
						}
					}		
			);
		
			return tweenAnimacionPlano;
		
	}
	
	function getAnimacionPlanoChaseReposition(indice, duracionTrayecto, verticeActual){
		var direccionProxPunto, rotY, pitch, nuevoPunto;
		
		var puntoActual;// = _listaFrames[indice-1];
		var puntoSig = _listaFrames[indice];
		if (indice > 0){
			puntoActual = _listaFrames[indice-1];
			 
		}else{
			alert("ver esto");
			puntoActual = _listaFrames[indice];
		}
		
		
			//calculo la direccion en que crece la escalera (proyectada en el plano XZ)
			 direccionProxPunto = new THREE.Vector3(
					puntoSig.x - puntoActual.x,
					0,
					puntoSig.y - puntoActual.y
				).normalize(); //proyectada en plano XZ
			
			 if (_listaFrames[indice].z < _ultimo_punto_linea_animada.z){ //escalera que baja
					direccionProxPunto.negate();
			 }
			 
			 rotY = Math.acos((new THREE.Vector3(1,0,0)).dot(direccionProxPunto));
								
				if (direccionProxPunto.x>=0){
					if (direccionProxPunto.z>=0){ rotY = -Math.PI/2 - rotY;}
					else{rotY = -Math.PI/2 + rotY;}
				}else{
					if (direccionProxPunto.z>=0){ rotY = Math.PI - (rotY - Math.PI/2);}
					else{
						rotY =  Math.PI/2 - (Math.PI - rotY)  ;}	
				}												
				
				
				var a = Math.abs(_controlCamaraFly.anguloEjeY) + Math.abs(rotY);
//				
			if (a > Math.PI){
				if (_controlCamaraFly.anguloEjeY < 0 && rotY>0 ){
					_controlCamaraFly.anguloEjeY += Math.PI*2;
				}else if (_controlCamaraFly.anguloEjeY > 0  && rotY<0){
					_controlCamaraFly.anguloEjeY -= Math.PI*2;
				}
			}	
				
							
		//calculo el punto al que va a para la camara
		nuevoPunto = direccionProxPunto.clone();
		nuevoPunto.multiplyScalar(PARAMS.distancia_a_escalera).addSelf(new THREE.Vector3(puntoActual.x, puntoActual.z, puntoActual.y));
		nuevoPunto.y += Math.abs(PARAMS.distancia_a_escalera);
		pitch = -Math.PI/4; //importante!! es PI/4 porque la distancia y altura al punto son iguales
								
		var tiempo = PARAMS.tiempo_posicionamiento_camara;
		if (true || _estadoAnimacion.getTipoAnimacionCamara() == "VARIABLE"){	
			var vectorDireccion = nuevoPunto.clone();
			vectorDireccion.subSelf(_controlCamaraFly.object.position);
			var dist = vectorDireccion.length();
			tiempo = dist/PARAMS.velocidadRepCamara;	
			
		}	
		
		var tweenPosicionarCamara = _controlCamaraFly.posicionarCamara(nuevoPunto, rotY, pitch, tiempo, null);
			
		
								
		
		//asigno punto a variable de contexto (global?)
		_ultimo_punto_linea_animada = _listaFrames[indice];
		
		//calculo la posicion final de la camara a medida que se realiza la animacion de la escalera
		//teniendo en cuenta la direccion en la que esta desplagada
		var otroPunto = direccionProxPunto.clone();
		otroPunto.multiplyScalar(PARAMS.distancia_a_escalera).addSelf(new THREE.Vector3(_listaFrames[indice].x,_listaFrames[indice].z,_listaFrames[indice].y));
		otroPunto.y += Math.abs(PARAMS.distancia_a_escalera);
		
	//	console.log("verticeActual",verticeActual);
	//	console.log("_listaFrames[indice]",_listaFrames[indice]);
	//	console.log("duracionTrayecto",duracionTrayecto);
		//la camara y la linea se animan en paralelo (el tiempo de reposicionamiento de la camara tiene que
		//ser igual al de la linea)
		var tweenAnimacionLineaCamara = new TWEEN.Tween( 
					{
						x: verticeActual.position.x,
						y: verticeActual.position.y,
						z: verticeActual.position.z,
						
						x_camara : nuevoPunto.x,
						y_camara : nuevoPunto.y,
						z_camara : nuevoPunto.z,
						angPitch: _controlCamaraFly.anguloPitch,
						angY: _controlCamaraFly.anguloEjeY,
					})
			.to(
					{	
						x: _listaFrames[indice].x,
						y: _listaFrames[indice].z + PARAMS.distancia_linea_a_plano,
						z: _listaFrames[indice].y,
						
						x_camara : otroPunto.x,
						y_camara : otroPunto.y,
						z_camara : otroPunto.z,
						//angPitch: pitch,
						//angY: rotY 
					},
					duracionTrayecto*1.5 )
			.easing( TWEEN.Easing.Linear.None )
			.onUpdate(function(){
				
				verticeActual.position.x = this.x;
				verticeActual.position.y = this.y;
				verticeActual.position.z = this.z;
				
				_lineaAnimada.geometry.__dirtyVertices = true;
				
			})
			.onComplete(
					function(){
						//if (indice < indiceFinal)
						{
							ANIMATOR.animarKeyframe(++indice);
						}
					});
		
		if (_estadoAnimacion.getTipoAnimacion() == "CHASE_ALL"){
			var tweenDummy = new TWEEN.Tween({}).to({}, 0)							
			.onComplete(function(){
					//tweenPosicionarCamara.start();
//				_controlCamaraFly.rotarCamara(rotY, pitch, duracionTrayecto, null).start();								
				tweenAnimacionLineaCamara.start();
			});	
			return tweenDummy;
		}

		//if (CHASE_REPOSITION_POINTS)
		tweenPosicionarCamara.chain(tweenAnimacionLineaCamara);
		return tweenPosicionarCamara;
		
		
		
	}
	
	
	function getAnimacionPlanoChase(indice, duracionTrayecto, verticeActual){
		var direccionProxPunto, rotY, pitch, nuevoPunto;
		
		var puntoActual;// = _listaFrames[indice-1];
		var puntoSig = _listaFrames[indice];
		if (indice > 0){
			puntoActual = _listaFrames[indice-1];
		}else{
			alert("ver esto");
			puntoActual = _listaFrames[indice];
		}
		
		
		//calculo la direccion en que crece la escalera (proyectada en el plano XZ)
		direccionProxPunto = new THREE.Vector3(
				puntoSig.x - puntoActual.x,
				0,
				puntoSig.y - puntoActual.y
		).normalize(); //proyectada en plano XZ
		
		//asigno punto a variable de contexto (global?)
		_ultimo_punto_linea_animada = _listaFrames[indice];
		
		//calculo la posicion final de la camara a medida que se realiza la animacion de la escalera
		//teniendo en cuenta la direccion en la que esta desplagada
		var otroPunto = direccionProxPunto.clone();
		otroPunto.multiplyScalar(PARAMS.distancia_a_escalera).addSelf(new THREE.Vector3(_listaFrames[indice].x,_listaFrames[indice].z,_listaFrames[indice].y));
		otroPunto.y += Math.abs(PARAMS.distancia_a_escalera);
		
		
		var tiempo = PARAMS.tiempo_posicionamiento_camara;
		if (true || _estadoAnimacion.getTipoAnimacionCamara() == "VARIABLE"){	
			var vectorDireccion = otroPunto.clone();
			vectorDireccion.subSelf(_controlCamaraFly.object.position);
			var dist = vectorDireccion.length();
			tiempo = dist/PARAMS.velocidadRepCamara;	
			
		}	
		
		//la camara y la linea se animan en paralelo (el tiempo de reposicionamiento de la camara tiene que
		//ser igual al de la linea)
		var tweenAnimacionLineaCamara = new TWEEN.Tween( 
				{
					x: verticeActual.position.x,
					y: verticeActual.position.y,
					z: verticeActual.position.z,
					
					x_camara : _controlCamaraFly.object.position.x,
					y_camara : _controlCamaraFly.object.position.y,
					z_camara : _controlCamaraFly.object.position.z,
				})
		.to(
				{	
					x: _listaFrames[indice].x,
					y: _listaFrames[indice].z + PARAMS.distancia_linea_a_plano,
					z: _listaFrames[indice].y,
					
					x_camara : otroPunto.x,
					y_camara : otroPunto.y,
					z_camara : otroPunto.z,									
				},
				tiempo/*duracionTrayecto*2.5*/ )
				.easing( TWEEN.Easing.Linear.None )
				.onUpdate(function(){
					
					verticeActual.position.x = this.x;
					verticeActual.position.y = this.y;
					verticeActual.position.z = this.z;
					
					if (_estadoAnimacion.getTipoAnimacion() == "CHASE_ALL"){
						_controlCamaraFly.object.position.x = this.x_camara;
						_controlCamaraFly.object.position.y = this.y_camara;
						_controlCamaraFly.object.position.z = this.z_camara;
						
							
						_controlCamaraFly.lookAtTarget(new THREE.Vector3(this.x,this.y,this.z));
					}
					
					_lineaAnimada.geometry.__dirtyVertices = true;
					
				})
				.onComplete(
						function(){
						//if (indice < indiceFinal)
							{
								ANIMATOR.animarKeyframe(++indice);
							}
						});
		
		
		//if (CHASE_REPOSITION_POINTS)
		return tweenAnimacionLineaCamara;
		
	}
	
	
	function getAnimacionPlanoLookAt(indice, duracionTrayecto, verticeActual){
		//armamos el tween para dibujar la linea
		//TWEEN: animamos la posicion del verticeActual desde las coordenadas del punto anterior al actual
		tweenLinea = new TWEEN.Tween( verticeActual.position )
		.to( {	x: _listaFrames[indice].x,
			y: _listaFrames[indice].z + PARAMS.distancia_linea_a_plano,
			z: _listaFrames[indice].y},
			duracionTrayecto )
			.easing( TWEEN.Easing.Linear.None )				
			.onComplete(
					//cuando finaliza la animacion, si existen mas _listaFrames de la polilinea (dentro del piso)
					// a animar, se continua animando el resto de los _listaFrames
					function(){
						//if (indice < indiceFinal)
						{
							ANIMATOR.animarKeyframe(++indice);
						}
					}		
			)
			.onUpdate(function(){
					_lineaAnimada.geometry.__dirtyVertices = true;
					_controlCamaraFly.lookAtTarget(new THREE.Vector3(this.x,this.y,this.z));
			});
								
		
		
		
		
		//CASO ESPECIAL
		//si true: es keyframe de piso (en este keyframe empieza la animacion de un nuevo piso)
		//si false: no es keyframe de piso, no reposicionamos la camara
		if (_listaFrames[indice].esKeyFramePiso && indice > 0){
			
			//armamos el tween para reposicionar la camara
			var duracionAnimCamara = PARAMS.tiempo_posicionamiento_camara;							
			
			var puntoTarget = new THREE.Vector3(_listaFrames[indice-1].x ,
					_listaFrames[indice-1].z + PARAMS.distancia_linea_a_plano,
					_listaFrames[indice-1].y );
			
			//<<aca iria el codigo para posicionar la camara en otro lugar relativoal primer punto de l piso>>	
			var puntoFinalCamara = puntoTarget.clone();//a que punto se reposiciona la camara
			var val = 500;
			puntoFinalCamara.y += val;
			puntoFinalCamara.z += val;
			
			if (_estadoAnimacion.getTipoAnimacionCamara() == "VARIABLE"){	
				var vectorDireccion = puntoFinalCamara.clone();
				vectorDireccion.subSelf(_controlCamaraFly.object.position);
				var dist = vectorDireccion.length();
				duracionAnimCamara = dist/PARAMS.velocidadRepCamara;	
				
			}							
									
			var tweenPosCamara = _controlCamaraFly.posicionarCamaraTarget(puntoFinalCamara, puntoTarget, duracionAnimCamara, null);
			
			tweenPosCamara.chain(tweenLinea);												
			return tweenPosCamara;												
			
		} 
		
		//no es keyframe de piso
		return tweenLinea;
		
	}
	
	
	this.restaurarEscalera = function(indice, color){
		var escaleraID = _listaFrames[indice].punteroNodo;
		var nombreEdificio = _listaFrames[indice].punteroEdificio;
		var nombrePiso = _listaFrames[indice].punteroPiso;
		var escalera = CONTROLLER_JERARQUIA_SPACES.getPortal3D(nombreEdificio, nombrePiso, escaleraID);//podria llamarse directamente sin pasar el nombre del edificio y el piso (el id del portal deberia ser unico)
		this.getAnimacionColorEscalera(escalera, color, 0);
//		getTweenColorEscalera(i,d);
	};
	
	
	
	function getTweenColorEscalera(indice, duracionTrayecto){
		//ANIMACION DE LOS ESCALONES
		//animo los colores de los escalones de la escalera
		
		//TODO getTweenColorEscaleras
		var escaleraID = _listaFrames[indice].punteroNodo;
		var nombreEdificio = _listaFrames[indice].punteroEdificio;
		var nombrePiso = _listaFrames[indice].punteroPiso;
		var escalera = CONTROLLER_JERARQUIA_SPACES.getPortal3D(nombreEdificio, nombrePiso, escaleraID);//podria llamarse directamente sin pasar el nombre del edificio y el piso (el id del portal deberia ser unico)
		
		//calculo la direccion en que se tienen que pintar los escalones de la escalera
		var direccion;
		if (!_ultimo_punto_linea_animada || _listaFrames[indice].z >= _ultimo_punto_linea_animada.z){
			if (escalera.type == "ustair") direccion = "up";
			else direccion = "down";
		}else{
			if (escalera.type == "dstair") direccion = "up";
			else direccion = "down";
		}
		
		var tweenColorEscalera = _this.getAnimacionColorEscalera(
				escalera,
				CONSTANTS.COLOR_ESCALON_ANIMADO, //CONSTANTS.COLOR_ESCALON_ANIMADO = var global 
				duracionTrayecto,
				direccion);
		//console.log("tweenColorEscalera ",tweenColorEscalera ); alert("tweenColorEscalera ");
		return 	tweenColorEscalera;
	}
	
	//<portal> : objeto THREE.mesh
	//si tiempo(ms) no esta definido (o es 0) no se anima (solo se cambia el color)
	//direccion: animar hacia arriba o abajo
	this.getAnimacionColorEscalera = function(portalEscalera, color, tiempo, direccion){
		var dir = (direccion != undefined)? direccion : "up";
		//console.log("tiempo",tiempo);
		var portal_to_animate = portalEscalera;
		switch (portalEscalera.type){
		case "ustair" :
		case "dstair" :
			
			if (tiempo){
				
				var ind = 0;
				var cant = portal_to_animate.cantEscalones;//esto esta harcodeado cuando creo el portal (y es de tipo escalera)
				var time_to_fire = (tiempo/(portal_to_animate.cantEscalones));
				var color_to_animate = color;
				
				//console.log("animando escalera. objeto 3d -->",this.objeto3D);
				if (dir == "up"){
					ind = 0;
					
					var tween = new TWEEN.Tween({})
						.to( {}, 0)							
						.onComplete(function(){
							if (ind < cant){
								for ( var i = 0; i < 6; i ++ ) {										
								    var face = portal_to_animate.geometry.faces[ ind*6 +i ];
								    face.color.setHex( color_to_animate );

								}
								ind++;
								portal_to_animate.geometry.__dirtyColors = true;
								
								tween.delay(time_to_fire).to({},0).start();
							}								
						})
					//.start();
						return tween;
				}else{
//					alert('no deberia haber entrado');
					ind = cant-1;
				
					var tween = new TWEEN.Tween({})
						.to({},0 )							
						.onComplete(function(){
							if (ind >= 0){
								for ( var i = 0; i < 6; i ++ ) {										
								    var face = portal_to_animate.geometry.faces[ ind*6 +i ];
								    face.color.setHex( color_to_animate );

								}
								portal_to_animate.geometry.__dirtyColors = true;
								ind--;
								//tambien se podria 
								tween.delay(time_to_fire).to({},0).start();
							}								
						});
					//.start();
					return tween;
				}
				
			}else{//cambiar valores sin animar
				for ( var i = 0, l = portal_to_animate.geometry.faces.length; i < l; i++ ) {										
				    portal_to_animate.geometry.faces[i].color.setHex(color);		  
				}
				portal_to_animate.geometry.__dirtyColors = true;
			}
			
			break;
		}
	};
};